/*
 * Copyright 2012 Claude Houle claude.houle@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.code.mochaccino.framework.webstrap.jetty;

import com.google.code.mochaccino.framework.crypto.EncryptionException;
import com.google.code.mochaccino.framework.crypto.keystore.KeyStoreManager;
import com.google.code.mochaccino.framework.crypto.keystore.KeyStoreType;
import com.google.code.mochaccino.framework.crypto.x509.X509ContextFactory;
import com.google.code.mochaccino.framework.crypto.x509.X509KeyManagerAggregator;
import com.google.code.mochaccino.framework.crypto.x509.X509TrustManagerAggregator;
import com.google.common.base.Preconditions;
import com.google.inject.AbstractModule;
import java.io.File;
import org.eclipse.jetty.ajp.Ajp13SocketConnector;
import org.eclipse.jetty.http.ssl.SslContextFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.SessionIdManager;
import org.eclipse.jetty.server.SessionManager;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.util.thread.ThreadPool;

/** A Jetty Module */
public final class JettyModule extends AbstractModule {

	private static final String[] INSECURE_CIPHER_SUITES = new String[] { "SSL_DH_anon_WITH_RC4_128_MD5",
	                                                                      "TLS_DH_anon_WITH_AES_128_CBC_SHA",
	                                                                      "TLS_DH_anon_WITH_AES_256_CBC_SHA",
	                                                                      "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA",
	                                                                      "SSL_DH_anon_WITH_DES_CBC_SHA",
	                                                                      "TLS_ECDH_anon_WITH_RC4_128_SHA",
	                                                                      "TLS_ECDH_anon_WITH_AES_128_CBC_SHA",
	                                                                      "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
	                                                                      "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA",
	                                                                      "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
	                                                                      "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA",
	                                                                      "TLS_ECDH_anon_WITH_NULL_SHA",
	                                                                      "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
	                                                                      "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
	                                                                      "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
	                                                                      "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
	                                                                      "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
	                                                                      "TLS_KRB5_EXPORT_WITH_RC4_40_MD5",
	                                                                      "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA",
	                                                                      "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5",
	                                                                      "SSL_RSA_WITH_NULL_MD5",
	                                                                      "SSL_RSA_WITH_NULL_SHA",
	                                                                      "TLS_ECDH_ECDSA_WITH_NULL_SHA",
	                                                                      "TLS_ECDH_RSA_WITH_NULL_SHA",
	                                                                      "TLS_ECDHE_ECDSA_WITH_NULL_SHA",
	                                                                      "TLS_ECDHE_RSA_WITH_NULL_SHA",
	                                                                      "SSL_RSA_WITH_RC4_128_MD5",
	                                                                      "TLS_KRB5_WITH_RC4_128_MD5",
	                                                                      "TLS_KRB5_WITH_3DES_EDE_CBC_MD5",
	                                                                      "TLS_KRB5_WITH_DES_CBC_MD5",
	                                                                      "SSL_RSA_WITH_DES_CBC_SHA",
	                                                                      "SSL_DHE_RSA_WITH_DES_CBC_SHA",
	                                                                      "SSL_DHE_DSS_WITH_DES_CBC_SHA",
	                                                                      "TLS_KRB5_WITH_DES_CBC_SHA",
	                                                                      "TLS_KRB5_WITH_RC4_128_SHA",
	                                                                      "TLS_KRB5_WITH_3DES_EDE_CBC_SHA",
	                                                                      "SSL_RSA_WITH_RC4_128_SHA",
	                                                                      "TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
	                                                                      "TLS_ECDH_RSA_WITH_RC4_128_SHA",
	                                                                      "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
	                                                                      "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
	                                                                      "TLS_RSA_WITH_AES_256_CBC_SHA",
	                                                                      "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
	                                                                      "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",
	                                                                      "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
	                                                                      "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
	                                                                      "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
	                                                                      "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
	                                                                      "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
	                                                                      "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
	                                                                      "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
	                                                                      "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
	                                                                      "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
	                                                                      "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
	                                                                      "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
	                                                                      "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA" };
	private Connector[] connectors;

	public JettyModule( Connector... connectors ) {
		this.connectors = Preconditions.checkNotNull( connectors );
		Preconditions.checkNotNull( this.connectors.length > 0 );
	}

	/**
	 * Produce a simple ajp connector on specified port. Default jetty configuration,
	 * useful for test-runs.
	 */
	public static final Connector AjpConnector( int port, String secretWord ) {
		Ajp13SocketConnector connector = new Ajp13SocketConnector();
		connector.setPort( port );
		connector.setAllowShutdown( false );
		connector.setSecretWord( secretWord );
		return connector;
	}

	/**
	 * Produce a simple http connector on specified port. Default jetty configuration,
	 * useful for test-runs.
	 */
	public static final Connector httpConnector( int port ) {
		SelectChannelConnector connector = new SelectChannelConnector();
		connector.setPort( port );
		return connector;
	}

	/**
	 * Produce a simple https connector on specified port with specified keystore. Default jetty
	 * configuration useful for tests-runs.
	 */
	public static final Connector httpsConnector( int port, String keyStore, KeyStoreType type, String keyStorePassword ) throws EncryptionException {
		KeyStoreManager ks = new KeyStoreManager( new File( keyStore ), type, keyStorePassword );
		ks.load();

		X509ContextFactory factory = new X509ContextFactory();
		X509KeyManagerAggregator key = new X509KeyManagerAggregator();
		key.addKeyManager( "RSA", ks );
		factory.add( key );

		X509TrustManagerAggregator trust = new X509TrustManagerAggregator();
		trust.addTrustManager( "RSA", ks );
		factory.add( trust );

		SslContextFactory sslFactory = new SslContextFactory();
		sslFactory.setSslContext( factory.getContext() );
		sslFactory.setExcludeCipherSuites( INSECURE_CIPHER_SUITES );

		SslSelectChannelConnector connector = new SslSelectChannelConnector( sslFactory );
		connector.setPort( port );

		return connector;
	}

	@Override
	protected void configure() {
		bind( ThreadPool.class ).toProvider( new ThreadPoolProvider() );
		bind( Connector[].class ).toInstance( this.connectors );
		bind( SessionManager.class ).toProvider( SessionManagerProvider.class ).asEagerSingleton();
		bind( SessionIdManager.class ).toProvider( SessionIdManagerProvider.class ).asEagerSingleton();
		bind( SessionHandler.class ).toProvider( SessionHandlerProvider.class ).asEagerSingleton();
		bind( Server.class ).toProvider( ServerProvider.class ).asEagerSingleton();
	}
}
